﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Manipulations;

namespace Black.SurfaceToolkit.Controls
{
    /// <summary>
    /// Conteneur pour le contrôle <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterView" />.
    /// </summary>
    [Browsable(true), Description("Conteneur d'élément d'un CanvasScatterView")]
    public class CanvasScatterViewItem : SurfaceContentControl
    {
        #region Propriétés de dépendance

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AngularDecelerationRate" />.
        /// </summary>
        public static readonly DependencyProperty AngularDecelerationRateProperty = DependencyProperty.Register("AngularDecelerationRate", typeof(double?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.CanMove" />.
        /// </summary>
        public static readonly DependencyProperty CanMoveProperty = DependencyProperty.Register("CanMove", typeof(bool?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.CanRotate" />.
        /// </summary>
        public static readonly DependencyProperty CanRotateProperty = DependencyProperty.Register("CanRotate", typeof(bool?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.CanScale" />.
        /// </summary>
        public static readonly DependencyProperty CanScaleProperty = DependencyProperty.Register("CanScale", typeof(bool?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Center" />.
        /// </summary>
        public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(CanvasScatterViewItem), new UIPropertyMetadata(new Point(double.NaN, double.NaN), new PropertyChangedCallback(CanvasScatterViewItem.OnCenterChanged)));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.DecelerationRate" />.
        /// </summary>
        public static readonly DependencyProperty DecelerationRateProperty = DependencyProperty.Register("DecelerationRate", typeof(double?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ExpansionDecelerationRate" />.
        /// </summary>
        public static readonly DependencyProperty ExpansionDecelerationRateProperty = DependencyProperty.Register("ExpansionDecelerationRate", typeof(double?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.FlingOutMode" />.
        /// </summary>
        public static readonly DependencyProperty FlingOutModeProperty = DependencyProperty.Register("FlingOutMode", typeof(FlingOutMode?), typeof(CanvasScatterViewItem), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsRender));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.IsFlingOutEnabled" />.
        /// </summary>
        public static readonly DependencyProperty IsFlingOutEnabledProperty = DependencyProperty.Register("IsFlingOutEnabled", typeof(bool?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.IsInertiaEnabled" />.
        /// </summary>
        public static readonly DependencyProperty IsInertiaEnabledProperty = DependencyProperty.Register("IsInertiaEnabled", typeof(bool?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Orientation" />.
        /// </summary>
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(double), typeof(CanvasScatterViewItem), new UIPropertyMetadata(double.NaN, new PropertyChangedCallback(CanvasScatterViewItem.OnOrientationChanged)));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Scale" />.
        /// </summary>
        public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(double), typeof(CanvasScatterViewItem), new UIPropertyMetadata(1.0, new PropertyChangedCallback(CanvasScatterViewItem.OnScaleChanged)));
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.SizeTransform" />.
        /// </summary>
        public static readonly DependencyProperty SizeTransformProperty = DependencyProperty.Register("SizeTransform", typeof(SizeTransformMode?), typeof(CanvasScatterViewItem), new UIPropertyMetadata(null));

        #endregion

        #region Evénements routés

        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationCompleted" />.
        /// </summary>
        public static readonly RoutedEvent AllOperationCompletedEvent = EventManager.RegisterRoutedEvent("AllOperationCompleted", RoutingStrategy.Bubble, typeof(OperationCompletedRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationDelta" />.
        /// </summary>
        public static readonly RoutedEvent AllOperationDeltaEvent = EventManager.RegisterRoutedEvent("AllOperationDelta", RoutingStrategy.Bubble, typeof(OperationDeltaRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationStarted" />.
        /// </summary>
        public static readonly RoutedEvent AllOperationStartedEvent = EventManager.RegisterRoutedEvent("AllOperationStarted", RoutingStrategy.Bubble, typeof(OperationStartedRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.FlingOutCompleted" />.
        /// </summary>
        public static readonly RoutedEvent FlingOutCompletedEvent = EventManager.RegisterRoutedEvent("FlingOutCompleted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaCompleted" />.
        /// </summary>
        public static readonly RoutedEvent InertiaCompletedEvent = EventManager.RegisterRoutedEvent("InertiaCompleted", RoutingStrategy.Bubble, typeof(OperationCompletedRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaDelta" />.
        /// </summary>
        public static readonly RoutedEvent InertiaDeltaEvent = EventManager.RegisterRoutedEvent("InertiaDelta", RoutingStrategy.Bubble, typeof(OperationDeltaRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaStarted" />.
        /// </summary>
        public static readonly RoutedEvent InertiaStartedEvent = EventManager.RegisterRoutedEvent("InertiaStarted", RoutingStrategy.Bubble, typeof(OperationStartedRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationCompleted" />.
        /// </summary>
        public static readonly RoutedEvent ManipulationCompletedEvent = EventManager.RegisterRoutedEvent("ManipulationCompleted", RoutingStrategy.Bubble, typeof(OperationCompletedRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationDelta" />.
        /// </summary>
        public static readonly RoutedEvent ManipulationDeltaEvent = EventManager.RegisterRoutedEvent("ManipulationDelta", RoutingStrategy.Bubble, typeof(OperationDeltaRoutedEventHandler), typeof(CanvasScatterViewItem));
        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationStarted" />.
        /// </summary>
        public static readonly RoutedEvent ManipulationStartedEvent = EventManager.RegisterRoutedEvent("ManipulationStarted", RoutingStrategy.Bubble, typeof(OperationStartedRoutedEventHandler), typeof(CanvasScatterViewItem));

        #endregion

        #region Evénéments

        /// <summary>
        /// Se produit lorsque tous les processeurs ont terminés leurs interventions.
        /// </summary>
        public event OperationCompletedRoutedEventHandler AllOperationCompleted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.AllOperationCompletedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.AllOperationCompletedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque l'un des processeurs produit une modification.
        /// </summary>
        public event OperationDeltaRoutedEventHandler AllOperationDelta
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.AllOperationDeltaEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.AllOperationDeltaEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque l'un des processeurs démarre.
        /// </summary>
        public event OperationStartedRoutedEventHandler AllOperationStarted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.AllOperationStartedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.AllOperationStartedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur d'inertie a terminé son intervention.
        /// </summary>
        public event OperationCompletedRoutedEventHandler InertiaCompleted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.InertiaCompletedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.InertiaCompletedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur d'inertie produit une modification.
        /// </summary>
        public event OperationDeltaRoutedEventHandler InertiaDelta
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.InertiaDeltaEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.InertiaDeltaEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur d'inertie démarre.
        /// </summary>
        public event OperationStartedRoutedEventHandler InertiaStarted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.InertiaStartedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.InertiaStartedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur de manipulation a terminé son intervention.
        /// </summary>
        public event OperationCompletedRoutedEventHandler ManipulationCompleted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.ManipulationCompletedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.ManipulationCompletedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur de manipulation produit une modification.
        /// </summary>
        public event OperationDeltaRoutedEventHandler ManipulationDelta
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.ManipulationDeltaEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.ManipulationDeltaEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsque le processeur de manipulation démarre.
        /// </summary>
        public event OperationStartedRoutedEventHandler ManipulationStarted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.ManipulationStartedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.ManipulationStartedEvent, value);
            }
        }

        /// <summary>
        /// Se produit lorsqu'un Fling Out se termine.
        /// </summary>
        public event RoutedEventHandler FlingOutCompleted
        {
            add
            {
                this.AddHandler(CanvasScatterViewItem.FlingOutCompletedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewItem.FlingOutCompletedEvent, value);
            }
        }

        #endregion

        #region Accesseurs

        /// <summary>
        /// Obtient ou définit la décélération angulaire pour l'inertie.
        /// </summary>
        [Browsable(true), DefaultValue(0.0000001), Description("Décélération angulaire pour l'inertie"), Category("CanvasScatterViewItem")]
        public double? AngularDecelerationRate
        {
            get
            {
                return ((double?)this.GetValue(CanvasScatterViewItem.AngularDecelerationRateProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.AngularDecelerationRateProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit une valeur indiquant si l'élément peut être déplacé.
        /// </summary>
        [Browsable(true), DefaultValue(true), Description("Valeur indiquant si l'élément peut être déplacé"), Category("CanvasScatterViewItem")]
        public bool? CanMove
        {
            get
            {
                return ((bool?)this.GetValue(CanvasScatterViewItem.CanMoveProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.CanMoveProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit une valeur indiquant si l'élément peut être tourné.
        /// </summary>
        [Browsable(true), DefaultValue(true), Description("Valeur indiquant si l'élément peut être tourné"), Category("CanvasScatterViewItem")]
        public bool? CanRotate
        {
            get
            {
                return ((bool?)this.GetValue(CanvasScatterViewItem.CanRotateProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.CanRotateProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit une valeur indiquant si l'élément peut être redimensionné.
        /// </summary>
        [Browsable(true), DefaultValue(true), Description("Valeur indiquant si l'élément peut être redimensionné"), Category("CanvasScatterViewItem")]
        public bool? CanScale
        {
            get
            {
                return ((bool?)this.GetValue(CanvasScatterViewItem.CanScaleProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.CanScaleProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le centre de l'élément.
        /// </summary>
        [Browsable(true), DefaultValue(typeof(Point), "0,0"), Description("Centre de l'élément"), Category("CanvasScatterViewItem")]
        public Point Center
        {
            get
            {
                return ((Point)this.GetValue(CanvasScatterViewItem.CenterProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.CenterProperty, value);
            }
        }

        /// <summary>
        /// Obtient le conteneur <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterView" /> parent.
        /// </summary>
        [Browsable(false)]
        internal CanvasScatterView Container
        {
            get
            {
                return ((CanvasScatterView)ItemsControl.ItemsControlFromItemContainer(this));
            }
        }

        /// <summary>
        /// Obtient ou définit le coéficient de décélération de l'intertie.
        /// </summary>
        [Browsable(true), DefaultValue(0.001), Description("Coéficient de décélération de l'intertie"), Category("CanvasScatterViewItem")]
        public double? DecelerationRate
        {
            get
            {
                return ((double?)this.GetValue(CanvasScatterViewItem.DecelerationRateProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.DecelerationRateProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le coéficient d'expansion de la décélération de l'intertie.
        /// </summary>
        [Browsable(true), DefaultValue(0.0001), Description("Coéficient d'expansion de la décélération de l'intertie"), Category("CanvasScatterViewItem")]
        public double? ExpansionDecelerationRate
        {
            get
            {
                return ((double?)this.GetValue(CanvasScatterViewItem.ExpansionDecelerationRateProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.ExpansionDecelerationRateProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le mode du Fling Out.
        /// </summary>
        [Browsable(true), DefaultValue(null), Description("Mode du fling out"), Category("CanvasScatterViewItem")]
        public FlingOutMode? FlingOutMode
        {
            get
            {
                return ((FlingOutMode?)this.GetValue(CanvasScatterViewItem.FlingOutModeProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.FlingOutModeProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit une valeur indiquant si le Fling Out sur l'élément est actif.
        /// </summary>
        [Browsable(true), DefaultValue(true), Description("Valeur indiquant si le Fling Out sur l'élément est actif"), Category("CanvasScatterViewItem")]
        public bool? IsFlingOutEnabled
        {
            get
            {
                return ((bool?)this.GetValue(CanvasScatterViewItem.IsFlingOutEnabledProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.IsFlingOutEnabledProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit une valeur indiquant si l'inertie est activée.
        /// </summary>
        [Browsable(true), DefaultValue(true), Description("Valeur indiquant si l'inertie est activée"), Category("CanvasScatterViewItem")]
        public bool? IsInertiaEnabled
        {
            get
            {
                return ((bool?)this.GetValue(CanvasScatterViewItem.IsInertiaEnabledProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.IsInertiaEnabledProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit l'orientation actuelle de l'élément.
        /// </summary>
        [Browsable(true), DefaultValue(0), Description("Orientation actuelle de l'élément"), Category("CanvasScatterViewItem")]
        public double Orientation
        {
            get
            {
                return ((double)this.GetValue(CanvasScatterViewItem.OrientationProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.OrientationProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit l'agrandissement actuel de l'élément.
        /// </summary>
        [Browsable(true), DefaultValue(1), Description("Agrandissement actuelle de l'élément"), Category("CanvasScatterViewItem")]
        public double Scale
        {
            get
            {
                return ((double)this.GetValue(CanvasScatterViewItem.ScaleProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.ScaleProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le mode de redimensionnement du contrôle.
        /// </summary>
        [Browsable(true), DefaultValue(null), Description("mode de redimensionnement du contrôle"), Category("CanvasScatterViewItem")]
        public SizeTransformMode? SizeTransform
        {
            get
            {
                return ((SizeTransformMode?)this.GetValue(CanvasScatterViewItem.SizeTransformProperty));
            }
            set
            {
                this.SetValue(CanvasScatterViewItem.SizeTransformProperty, value);
            }
        }

        #endregion

        #region Variables d'instance

        private bool m_bCenterInteralAffectation;
        private bool m_bInFlingOut;
        private bool m_bInternalLoad;
        private Point m_oManipulationOrigin;
        private Affine2DInertiaProcessor m_oInertia;
        private Affine2DManipulationProcessor m_oTraker;

        #endregion

        #region Variables de classe

        private static Random _oRand;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Initialise la classe <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterViewItem" />.
        /// </summary>
        static CanvasScatterViewItem()
        {
            FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(CanvasScatterViewItem), new FrameworkPropertyMetadata(typeof(CanvasScatterViewItem)));

            // Initialise le générateur de nombres pseudo-aléatoires

            CanvasScatterViewItem._oRand = new Random();
        }

        /// <summary>
        /// Initialise une nouvelle instance de <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterViewItem" />.
        /// </summary>
        public CanvasScatterViewItem()
            : this(false)
        {
        }

        /// <summary>
        /// Initialise une nouvelle instance de <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterViewItem" />.
        /// </summary>
        /// <param name="p_bInternalLoad">Valeur indiquant si le contôle a été auto-généré.</param>
        internal CanvasScatterViewItem(bool p_bInternalLoad)
            : base()
        {
            // Initialisation custom

            this.m_oTraker = null;
            this.m_oInertia = null;
            this.m_bInternalLoad = p_bInternalLoad;
            this.m_bInFlingOut = false;
            this.m_bCenterInteralAffectation = false;
            this.m_oManipulationOrigin = new Point(double.NaN, double.NaN);

            // Branchement événement

            base.Loaded += new RoutedEventHandler(this.CanvasScatterViewItem_Loaded);
        }

        #endregion

        #region Evénements XAML

        /// <summary>
        /// Appelée lorsque le contrôle est chargé.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void CanvasScatterViewItem_Loaded(object p_oSender, RoutedEventArgs p_oArgs)
        {
            // On initialise les transformations utilent aux manipulations

            this.InitializeTransformations();

            // Initialisation des éléments de traking

            this.InitializeTrakerAndPosition();
        }

        /// <summary>
        /// Appelée lorsque le processeur d'inertie démarre.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Inertia_Affine2DInertiaCompleted(object p_oSender, Affine2DOperationCompletedEventArgs p_oArgs)
        {
            // Si pas en fling out et en dehors de l'écran on le recentre

            if (!this.m_bInFlingOut)
                this.PositionOutRestore();

            // On raise l'event

            this.RaiseInertiaCompletedEvent(p_oArgs);
            this.RaiseAllOperationCompletedEvent(p_oArgs);
        }

        /// <summary>
        /// Appelée lorsque processeur d'inertie produit une modification.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Inertia_Affine2DInertiaDelta(object p_oSender, Affine2DOperationDeltaEventArgs p_oArgs)
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            bool bContainerIsFlingOutEnabled = false;

            if (this.Container != null)
            {
                bContainerIsFlingOutEnabled = this.Container.IsFlingOutEnabled;
            }

            // Translation de l'objet

            this.Center = new Point(this.Center.X + p_oArgs.Delta.X, this.Center.Y + p_oArgs.Delta.Y);

            // Gestion du Fling Out

            if (this.IsFlingOutEnabled ?? bContainerIsFlingOutEnabled)
                this.OutScreen();

            // Test rebond si pas de fling out

            if (!this.m_bInFlingOut)
                this.ItemScreenLimit(p_oArgs.Velocity, p_oArgs.AngularVelocity);

            // Définition du pivot

            this.m_oTraker.PivotPoint = this.GetManipulationPivot();

            // On rafraichis la coordonnée de centre

            this.SetCenterNoRefresh(this.GetManipulationPivot());

            // On raise l'event

            this.RaiseInertiaDeltaEvent(p_oArgs);
            this.RaiseAllOperationDeltaEvent(p_oArgs);
        }

        /// <summary>
        /// Appelée à la fin d'une manipulation.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Traker_Affine2DManipulationCompleted(object p_oSender, Affine2DOperationCompletedEventArgs p_oArgs)
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            bool bContainerCanMove = false;
            bool bContainerIsInertiaEnabled = false;
            double dContainerDecelerationRate = 1.0;
            double dContainerExpansionDecelerationRate = 0.0;
            double dContainerAngularDecelerationRate = 1.0;

            if (this.Container != null)
            {
                bContainerCanMove = this.Container.CanMove;
                bContainerIsInertiaEnabled = this.Container.IsInertiaEnabled;
                dContainerDecelerationRate = this.Container.DecelerationRate;
                dContainerExpansionDecelerationRate = this.Container.ExpansionDecelerationRate;
                dContainerAngularDecelerationRate = this.Container.AngularDecelerationRate;
            }

            // On raise l'event

            this.RaiseManipulationCompletedEvent(p_oArgs);

            // Inertie activée ?

            if (!(this.CanMove ?? bContainerCanMove))
            {
                this.PositionOutRestore();
                this.RaiseAllOperationCompletedEvent(p_oArgs);
            }
            else if (!(this.IsInertiaEnabled ?? bContainerIsInertiaEnabled))
            {
                this.PositionOutRestore();
                this.RaiseAllOperationCompletedEvent(p_oArgs);
            }
            else
            {
                // Affectation des paramètres de l'inertie

                this.m_oInertia.InitialAngularVelocity = p_oArgs.AngularVelocity;
                this.m_oInertia.InitialExpansionVelocity = p_oArgs.ExpansionVelocity;
                this.m_oInertia.InitialOrigin = p_oArgs.ManipulationOrigin;
                this.m_oInertia.InitialVelocity = p_oArgs.Velocity;

                this.m_oInertia.DesiredDeceleration = this.DecelerationRate ?? dContainerDecelerationRate;
                this.m_oInertia.DesiredExpansionDeceleration = this.ExpansionDecelerationRate ?? dContainerExpansionDecelerationRate;
                this.m_oInertia.DesiredAngularDeceleration = this.AngularDecelerationRate ?? dContainerAngularDecelerationRate;

                // Lancement de l'inertie

                this.m_oInertia.Begin();

                // On raise l'event

                this.RaiseInertiaStartedEvent(p_oArgs.ManipulationOrigin);
            }
        }

        /// <summary>
        /// Appelée lorsque processeur de manipulation produit une modification.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Traker_Affine2DManipulationDelta(object p_oSender, Affine2DOperationDeltaEventArgs p_oArgs)
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            bool bContainerCanRotate = false;
            bool bContainerCanScale = false;
            bool bContainerCanMove = false;

            if (this.Container != null)
            {
                bContainerCanRotate = this.Container.CanRotate;
                bContainerCanScale = this.Container.CanScale;
                bContainerCanMove = this.Container.CanMove;
            }

            // Sauvegarde du oPoint de manipulation

            this.m_oManipulationOrigin = p_oArgs.ManipulationOrigin;

            // Rotation de l'objet

            if (this.CanRotate ?? bContainerCanRotate && p_oArgs.RotationDelta != 0)
                this.Orientation += p_oArgs.RotationDelta;

            // Scale de l'objet

            if (this.CanScale ?? bContainerCanScale && p_oArgs.ScaleDelta != this.Scale)
                this.Scale = this.Scale * p_oArgs.ScaleDelta;

            // Translation de l'objet

            if (this.CanMove ?? bContainerCanMove && (p_oArgs.Delta.X != 0 || p_oArgs.Delta.Y != 0))
                this.Center = new Point(this.Center.X + p_oArgs.Delta.X, this.Center.Y + p_oArgs.Delta.Y);

            // Définition du pivot

            this.m_oTraker.PivotPoint = this.GetManipulationPivot();

            // On rafraichis la coordonnée de centre

            this.SetCenterNoRefresh(this.GetManipulationPivot());

            // On test la position

            this.PositionOutRestore();

            // On raise l'event

            this.RaiseManipulationDeltaEvent(p_oArgs);
            this.RaiseAllOperationDeltaEvent(p_oArgs);
        }

        /// <summary>
        /// Appelée lorsque le processeur de manipulation démarre.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Traker_Affine2DManipulationStarted(object p_oSender, Affine2DOperationStartedEventArgs p_oArgs)
        {
            // Définition du pivot

            this.m_oTraker.PivotPoint = p_oArgs.ManipulationOrigin;

            // On raise l'event

            this.RaiseManipulationStartedEvent(p_oArgs.ManipulationOrigin);
            this.RaiseAllOperationStartedEvent(p_oArgs.ManipulationOrigin);
        }

        #endregion

        #region Fonctions publiques

        /// <summary>
        /// Affiche l'élément au premier plan de son calque.
        /// </summary>
        public void BringOnTop()
        {
            // On met en avant l'item

            if (this.Container != null)
                this.Container.RefreshZIndexes(this);
        }

        /// <summary>
        /// Réinitialise les transforamtions.
        /// </summary>
        public void ClearTransform()
        {
            this.Orientation = 0.0;
            this.Scale = 1.0;
        }

        #endregion

        #region Fonctions protégées

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Center" /> est modifiée.
        /// </summary>
        /// <param name="p_oOld">Ancienne valeur.</param>
        /// <param name="p_oNew">Nouvelle valeur.</param>
        protected virtual void OnCenterChanged(Point p_oOld, Point p_oNew)
        {
            // Si affectation interne on ne fait rien

            if (this.m_bCenterInteralAffectation)
                return;

            // Filtrage sur la valeur NaN

            if (double.IsNaN(p_oOld.X))
                p_oOld.X = 0.0;

            if (double.IsNaN(p_oOld.Y))
                p_oOld.Y = 0.0;

            // Récupération de la matrice actuelle

            Matrix oActual = (this.RenderTransform as MatrixTransform).Matrix;

            // On définit la transformation

            oActual.Translate(p_oNew.X - p_oOld.X, p_oNew.Y - p_oOld.Y);

            // On redéfinit la matrice

            this.RenderTransform = new MatrixTransform(oActual);

            // Gestion du oPoint d'origine si non existant

            if (double.IsNaN(this.m_oManipulationOrigin.X) || double.IsNaN(this.m_oManipulationOrigin.Y))
                this.m_oManipulationOrigin = this.Center;

            // Définition du pivot

            if (this.m_oTraker != null)
                this.m_oTraker.PivotPoint = this.GetManipulationPivot();
        }

        /// <summary>
        /// Appelée lors d'un contact.
        /// </summary>
        /// <param name="p_oArgs">Données de l'événement.</param>
        protected override void OnContactDown(ContactEventArgs p_oArgs)
        {
            base.OnContactDown(p_oArgs);

            // On met en avant l'item

            if (this.Container != null)
                this.Container.RefreshZIndexes(this);

            // On démare le tracking

            if (p_oArgs.Contact.IsFingerRecognized && this.m_oTraker != null)
            {
                this.m_oTraker.BeginTrack(p_oArgs.Contact);

                // Mise à jour capture contact

                p_oArgs.Handled = true;
            }
        }

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Orientation" /> est modifiée.
        /// </summary>
        /// <param name="p_dOld">Ancienne valeur.</param>
        /// <param name="p_dNew">Nouvelle valeur.</param>
        protected virtual void OnOrientationChanged(double p_dOld, double p_dNew)
        {
            // Filtrage sur la valeur NaN

            if (double.IsNaN(p_dOld))
                p_dOld = 0.0;

            if (double.IsNaN(p_dNew))
                p_dNew = 0.0;

            // Récupération de la matrice actuelle

            Matrix oActual = (this.RenderTransform as MatrixTransform).Matrix;

            // On définit la transformation

            if (this.CanMove ?? (this.Container != null && this.Container.CanMove))
                oActual.RotateAt(p_dNew - p_dOld, this.m_oManipulationOrigin.X, this.m_oManipulationOrigin.Y);
            else
                oActual.RotateAt(p_dNew - p_dOld, this.Center.X, this.Center.Y);

            // On redéfinit la matrice

            this.RenderTransform = new MatrixTransform(oActual);
        }

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.Scale" /> est modifiée.
        /// </summary>
        /// <param name="p_dOld">Ancienne valeur.</param>
        /// <param name="p_dNew">Nouvelle valeur.</param>
        protected virtual void OnScaleChanged(double p_dOld, double p_dNew)
        {
            double dNewScale = 1;

            // Filtrage sur la valeur NaN

            if (double.IsNaN(p_dOld) || p_dOld == 0)
                p_dOld = 1;

            if (double.IsNaN(p_dNew))
                p_dNew = 1;

            // Calcul du facteur

            dNewScale = p_dNew / p_dOld;

            // Récupération de la matrice actuelle

            Matrix oActual = (this.RenderTransform as MatrixTransform).Matrix;

            // On définit la transformation

            oActual.ScaleAt(dNewScale, dNewScale, this.m_oManipulationOrigin.X, this.m_oManipulationOrigin.Y);

            // On redéfinit la matrice

            this.RenderTransform = new MatrixTransform(oActual);
        }

        /// <summary>
        /// Appelée pour rendre l'élément.
        /// </summary>
        /// <param name="p_oDrawingContext">Contexte d'affichage.</param>
        protected override void OnRender(DrawingContext p_oDrawingContext)
        {
            base.OnRender(p_oDrawingContext);

            // Affichage du fond

            p_oDrawingContext.DrawRectangle(this.Background, new Pen(this.Background, 0), new Rect(0, 0, base.ActualWidth, base.ActualHeight));
        }

        /// <summary>
        /// Appelée lorsque les dimensions sont modifiées.
        /// </summary>
        /// <param name="p_oSizeInfo">Informations sur le redimensionnement.</param>
        protected override void OnRenderSizeChanged(SizeChangedInfo p_oSizeInfo)
        {
            // Appel à la source

            base.OnRenderSizeChanged(p_oSizeInfo);

            // Vérification

            if (!this.IsLoaded)
                return;

            // Déterminaction du mode

            SizeTransformMode eMode = this.Container.SizeTransform;

            if (this.SizeTransform.HasValue)
                eMode = this.SizeTransform.Value;

            // On repositionne le centre en fonction du mode

            if (eMode == SizeTransformMode.Centered)
            {
                this.ResizeCentered(p_oSizeInfo);
            }
            else
            {
                this.ResizeContinue(p_oSizeInfo);
            }

            // Modification du pivot

            this.m_oTraker.PivotRadius = Math.Max((double)1.0, (double)(Math.Max(base.ActualWidth, base.ActualHeight) / 2.0));
        }

        /// <summary>
        /// Définit une nouvelle valeur pour le centre de l'élément sans en mettre à jour son centre.
        /// </summary>
        /// <param name="p_oNewCenter">Nouveau centre.</param>
        protected void SetCenterNoRefresh(Point p_oNewCenter)
        {
            this.m_bCenterInteralAffectation = true;
            this.Center = p_oNewCenter;
            this.m_bCenterInteralAffectation = false;
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Crée le storyboard pour le Fling Out et le lance.
        /// </summary>
        /// <param name="p_oGoTo">Destination.</param>
        private void CreateAndStartFlingOutStoryboard(Point p_oGoTo)
        {
            this.m_bInFlingOut = true;

            // Arrêt manipulation

            this.m_oInertia.End();

            // Création du Stroyboard

            Storyboard oStoryboard = new Storyboard();

            DoubleAnimation oAnimationAlpha = new DoubleAnimation(0, new Duration(new TimeSpan(0, 0, 0, 0, 200)));
            PointAnimation oAnimationPoint = new PointAnimation(this.Center, p_oGoTo, new Duration(new TimeSpan(0, 0, 0, 0, 500)));

            Storyboard.SetTarget(oAnimationAlpha, this);
            Storyboard.SetTarget(oAnimationPoint, this);

            Storyboard.SetTargetProperty(oAnimationAlpha, new PropertyPath(UIElement.OpacityProperty));
            Storyboard.SetTargetProperty(oAnimationPoint, new PropertyPath(CanvasScatterViewItem.CenterProperty));

            oStoryboard.Children.Add(oAnimationAlpha);
            oStoryboard.Children.Add(oAnimationPoint);

            // On se branche sur la fin du storyboard

            oStoryboard.Completed += new EventHandler(this.StoryBoardComplete);

            // Démarage du storyboard

            this.BeginStoryboard(oStoryboard);
        }

        /// <summary>
        /// Obtient le rectangle englobant de cet élément.
        /// </summary>
        /// <returns>Rectangle englobant de cet élément.</returns>
        private Rect GetBounds()
        {
            // On récupère la transformation générale du contrôle

            GeneralTransform oTransform = this.Container == null ? MatrixTransform.Identity : this.TransformToVisual(this.Container);

            // On calcul et on retourne ses Bounds

            return (oTransform.TransformBounds(new Rect(0, 0, this.ActualWidth, this.ActualHeight)));
        }

        /// <summary>
        /// Obtient le pivot de cet élément.
        /// </summary>
        /// <returns>Pivot de cet élément.</returns>
        private Point GetManipulationPivot()
        {
            Rect oBounds = this.GetBounds();

            // On retourne le oPoint de pivot

            return (new Point((oBounds.Left + oBounds.Right) / 2, (oBounds.Top + oBounds.Bottom) / 2));
        }

        /// <summary>
        /// Obtient un nouveau centre aléatoire.
        /// </summary>
        /// <returns>Nouveau centre aléatoire.</returns>
        private Point GetRandomCenter()
        {
            int iX = 0;
            int iY = 0;

            // Détermination d'un emplacement au hasard

            Point oCenter = new Point();

            if (this.Container != null)
            {
                // Initialisation

                iX = Convert.ToInt32(this.Container.ActualWidth);
                iY = Convert.ToInt32(this.Container.ActualHeight);

                // Vérification des informations

                if (!double.IsNaN(this.Container.Width) && !double.IsNaN(this.Container.Height) && (iX <= 0 || iY <= 0))
                {
                    iX = Convert.ToInt32(this.Container.Width);
                    iY = Convert.ToInt32(this.Container.Height);
                }

                // Calcul du centre

                double dNextX;
                double dNextY;
                lock (CanvasScatterViewItem._oRand)
                {
                    dNextX = Convert.ToDouble(CanvasScatterViewItem._oRand.Next(0, iX));
                    dNextY = Convert.ToDouble(CanvasScatterViewItem._oRand.Next(0, iY));
                }

                oCenter.X = dNextX;
                oCenter.Y = dNextY;
            }

            // On retourne la coordonnée

            return (oCenter);
        }

        /// <summary>
        /// Obtient une nouvelle orientation aléatoire.
        /// </summary>
        /// <returns>Nouvelle orientation aléatoire.</returns>
        private double GetRandomOrientation()
        {
            double dNextOrientation;
            lock (CanvasScatterViewItem._oRand)
            {
                dNextOrientation = Convert.ToDouble(CanvasScatterViewItem._oRand.Next(0, 360));
            }

            // On retourne la oRotation

            return (dNextOrientation);
        }

        /// <summary>
        /// Initialise les suiveurs
        /// </summary>
        private void InitializeTrakerAndPosition()
        {
            // Initialisation manipulation

            this.m_oTraker = new Affine2DManipulationProcessor(Affine2DManipulations.Rotate | Affine2DManipulations.Scale | Affine2DManipulations.TranslateX | Affine2DManipulations.TranslateY, this.Container);
            this.m_oTraker.PivotRadius = Math.Max((double)1.0, (double)(Math.Max(base.ActualWidth, base.ActualHeight) / 2.0));
            this.m_oTraker.Affine2DManipulationCompleted += new System.EventHandler<Affine2DOperationCompletedEventArgs>(this.Traker_Affine2DManipulationCompleted);
            this.m_oTraker.Affine2DManipulationDelta += new System.EventHandler<Affine2DOperationDeltaEventArgs>(this.Traker_Affine2DManipulationDelta);
            this.m_oTraker.Affine2DManipulationStarted += new System.EventHandler<Affine2DOperationStartedEventArgs>(this.Traker_Affine2DManipulationStarted);

            // Initialisation inertie

            this.m_oInertia = new Affine2DInertiaProcessor();
            this.m_oInertia.Affine2DInertiaCompleted += new EventHandler<Affine2DOperationCompletedEventArgs>(this.Inertia_Affine2DInertiaCompleted);
            this.m_oInertia.Affine2DInertiaDelta += new System.EventHandler<Affine2DOperationDeltaEventArgs>(this.Inertia_Affine2DInertiaDelta);

            // Initialisation position

            if (this.m_bInternalLoad)
            {
                // Affectation position

                if (Double.IsNaN(this.Center.X) || Double.IsNaN(this.Center.Y))
                    this.Center = this.GetRandomCenter();

                if (Double.IsNaN(this.Orientation))
                    this.Orientation = this.GetRandomOrientation();
            }
        }

        /// <summary>
        /// Initialise les transformations utiles aux manipulations.
        /// </summary>
        private void InitializeTransformations()
        {
            // Création du oPoint d'origine

            Matrix matrice = new Matrix(1, 0, 0, 1, (this.ActualWidth / 2) * -1, (this.ActualHeight / 2) * -1);

            // Réapplication des informations

            if (!double.IsNaN(this.m_oManipulationOrigin.X) && !double.IsNaN(this.m_oManipulationOrigin.Y))
            {
                if (!double.IsNaN(this.Orientation))
                    matrice.RotateAt(this.Orientation, this.m_oManipulationOrigin.X, this.m_oManipulationOrigin.Y);

                if (!double.IsNaN(this.Scale))
                    matrice.ScaleAt(this.Scale, this.Scale, this.m_oManipulationOrigin.X, this.m_oManipulationOrigin.Y);

                if (!double.IsNaN(this.Center.X) && !double.IsNaN(this.Center.Y))
                    matrice.Translate(this.Center.X, this.Center.Y);
            }

            // On définit la nouvelle matrice

            this.RenderTransform = new MatrixTransform(matrice);
        }

        /// <summary>
        /// Obtient une valeur indiquant si l'élément est en collision avec un bord.
        /// </summary>
        /// <param name="p_oVelocity">Vélocité actuelle.</param>
        /// <param name="p_dAngularVelocity">Vélocité angulaire actuelle.</param>
        /// <returns><see langword="true" /> s'il y a collision avec un bord, <see langword="false" /> sinon.</returns>
        private bool ItemScreenLimit(Vector p_oVelocity, double p_dAngularVelocity)
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            double dContainerActualWidth = double.PositiveInfinity;
            double dContainerActualHeight = double.PositiveInfinity;

            if (this.Container != null)
            {
                dContainerActualWidth = this.Container.ActualWidth;
                dContainerActualHeight = this.Container.ActualHeight;
            }

            Point oCenter = this.Center;
            Controls.FlingOutMode eMode = Controls.FlingOutMode.None;

            // Test sortie de l'élément

            if (oCenter.X < 0)
            {
                eMode = Controls.FlingOutMode.Left;
            }
            else if (oCenter.X > dContainerActualWidth)
            {
                eMode = Controls.FlingOutMode.Right;
            }

            if (oCenter.Y < 0)
            {
                eMode |= Controls.FlingOutMode.Up;
            }
            else if (oCenter.Y > dContainerActualHeight)
            {
                eMode |= Controls.FlingOutMode.Down;
            }

            // On modifie l'inertie

            if ((eMode & Controls.FlingOutMode.Left) == Controls.FlingOutMode.Left
             || (eMode & Controls.FlingOutMode.Right) == Controls.FlingOutMode.Right
             || (eMode & Controls.FlingOutMode.Up) == Controls.FlingOutMode.Up
             || (eMode & Controls.FlingOutMode.Down) == Controls.FlingOutMode.Down)
            {
                this.ReCreateInertia(new Vector(p_oVelocity.X, p_oVelocity.Y), p_dAngularVelocity, eMode);
                return (true);
            }

            // Pas de collision sur bord

            return (false);
        }

        /// <summary>
        /// Obtient une valeur indiquant si l'élément est en dehors de l'écran.
        /// </summary>
        /// <returns><see langword="true" /> si l'élément est en dehors de l'écran, <see langword="false" /> sinon.</returns>
        private bool OutScreen()
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            FlingOutMode eContainerFlingOutMode = Controls.FlingOutMode.None;
            double dContainerActualWidth = double.PositiveInfinity;
            double dContainerActualHeight = double.PositiveInfinity;

            if (this.Container != null)
            {
                eContainerFlingOutMode = this.Container.FlingOutMode;
                dContainerActualWidth = this.Container.ActualWidth;
                dContainerActualHeight = this.Container.ActualHeight;
            }

            Point oCenter = this.Center;

            // Récupération du mode fling out

            Controls.FlingOutMode eMode = this.FlingOutMode ?? eContainerFlingOutMode;

            // Test sortie de l'élément

            if (oCenter.X < 0 && (Controls.FlingOutMode.Left & eMode) == Controls.FlingOutMode.Left)
            {
                this.CreateAndStartFlingOutStoryboard(new Point(oCenter.X - 100, oCenter.Y));
                return (true);
            }
            else if (oCenter.X > dContainerActualWidth && (Controls.FlingOutMode.Right & eMode) == Controls.FlingOutMode.Right)
            {
                this.CreateAndStartFlingOutStoryboard(new Point(oCenter.X + 100, oCenter.Y));
                return (true);
            }
            else if (oCenter.Y < 0 && (Controls.FlingOutMode.Up & eMode) == Controls.FlingOutMode.Up)
            {
                this.CreateAndStartFlingOutStoryboard(new Point(oCenter.X, oCenter.Y - 100));
                return (true);
            }
            else if (oCenter.Y > dContainerActualHeight && (Controls.FlingOutMode.Down & eMode) == Controls.FlingOutMode.Down)
            {
                this.CreateAndStartFlingOutStoryboard(new Point(oCenter.X, oCenter.Y + 100));
                return (true);
            }

            // Pas de sortie

            return (false);
        }

        /// <summary>
        /// Restaure la position de l'élément s'il est en dehors du <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterView" />.
        /// </summary>
        private void PositionOutRestore()
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            double dContainerActualWidth = double.PositiveInfinity;
            double dContainerActualHeight = double.PositiveInfinity;

            if (this.Container != null)
            {
                dContainerActualWidth = this.Container.ActualWidth;
                dContainerActualHeight = this.Container.ActualHeight;
            }

            Point oCenter = this.Center;
            Point oPosition = new Point(this.Center.X, this.Center.Y);

            // Vérification de la sortie

            if ((oCenter.X + this.ActualWidth) < 0)
            {
                oPosition.X = (this.ActualWidth * -1) + 40;
            }
            else if ((oCenter.X - this.ActualWidth) > dContainerActualWidth)
            {
                oPosition.X = dContainerActualWidth + this.ActualWidth - 40;
            }

            if ((oCenter.Y + this.ActualHeight) < 0)
            {
                oPosition.Y = (this.ActualHeight * -1) + 40;
            }
            else if ((oCenter.Y - this.ActualHeight) > dContainerActualHeight)
            {
                oPosition.Y = dContainerActualHeight + this.ActualHeight - 40;
            }

            // Réaffectation position

            this.Center = oPosition;
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationCompleted" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseAllOperationCompletedEvent(Affine2DOperationCompletedEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationCompletedRoutedEventArgs(CanvasScatterViewItem.AllOperationCompletedEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationDelta" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseAllOperationDeltaEvent(Affine2DOperationDeltaEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationDeltaRoutedEventArgs(CanvasScatterViewItem.AllOperationDeltaEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.AllOperationStarted" />.
        /// </summary>
        /// <param name="p_oOrigin">Origine de la manipulation.</param>
        private void RaiseAllOperationStartedEvent(Point p_oOrigin)
        {
            this.RaiseEvent(new OperationStartedRoutedEventArgs(CanvasScatterViewItem.AllOperationStartedEvent, p_oOrigin));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaCompleted" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseInertiaCompletedEvent(Affine2DOperationCompletedEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationCompletedRoutedEventArgs(CanvasScatterViewItem.InertiaCompletedEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaDelta" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseInertiaDeltaEvent(Affine2DOperationDeltaEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationDeltaRoutedEventArgs(CanvasScatterViewItem.InertiaDeltaEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.InertiaStarted" />.
        /// </summary>
        /// <param name="p_oOrigin">Origine de la manipulation.</param>
        private void RaiseInertiaStartedEvent(Point p_oOrigin)
        {
            this.RaiseEvent(new OperationStartedRoutedEventArgs(CanvasScatterViewItem.InertiaStartedEvent, p_oOrigin));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationCompleted" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseManipulationCompletedEvent(Affine2DOperationCompletedEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationCompletedRoutedEventArgs(CanvasScatterViewItem.ManipulationCompletedEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationDelta" />.
        /// </summary>
        /// <param name="p_oOperation">Source d'information.</param>
        private void RaiseManipulationDeltaEvent(Affine2DOperationDeltaEventArgs p_oOperation)
        {
            this.RaiseEvent(new OperationDeltaRoutedEventArgs(CanvasScatterViewItem.ManipulationDeltaEvent, p_oOperation));
        }

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.ManipulationStarted" />.
        /// </summary>
        /// <param name="p_oOrigin">Origine de la manipulation.</param>
        private void RaiseManipulationStartedEvent(Point p_oOrigin)
        {
            this.RaiseEvent(new OperationStartedRoutedEventArgs(CanvasScatterViewItem.ManipulationStartedEvent, p_oOrigin));
        }

        /// <summary>
        /// Déclenche l'événement <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewItem.FlingOutCompleted" />.
        /// </summary>
        private void RaiseFlingOutCompletedEvent()
        {
            this.RaiseEvent(new RoutedEventArgs(CanvasScatterViewItem.FlingOutCompletedEvent, RoutedEventArgs.Empty));
        }

        /// <summary>
        /// Recrée l'intertie pour le rebond.
        /// </summary>
        /// <param name="p_oVelocity">Valeur de la vélocité actuelle.</param>
        /// <param name="p_dAngularVelocity">Valeur de la vélocité angulaire actuelle.</param>
        /// <param name="p_eBord">Bords en collision.</param>
        private void ReCreateInertia(Vector p_oVelocity, double p_dAngularVelocity, FlingOutMode p_eBord)
        {
            // On récupère les proprités du conteneur (s'il est disponible)

            double dContainerActualWidth = 2.0;
            double dContainerActualHeight = 2.0;

            if (this.Container != null)
            {
                dContainerActualWidth = this.Container.ActualWidth;
                dContainerActualHeight = this.Container.ActualHeight;
            }

            Point oOrigine = new Point(this.Center.X, this.Center.Y);

            // On stop l'inertie

            this.m_oInertia.Affine2DInertiaCompleted -= new EventHandler<Affine2DOperationCompletedEventArgs>(this.Inertia_Affine2DInertiaCompleted);
            this.m_oInertia.Affine2DInertiaDelta -= new System.EventHandler<Affine2DOperationDeltaEventArgs>(this.Inertia_Affine2DInertiaDelta);

            this.m_oInertia.End();

            // Calcul du nouveau vecteur de déplacement

            if ((p_eBord & Controls.FlingOutMode.Left) == Controls.FlingOutMode.Left
             || (p_eBord & Controls.FlingOutMode.Right) == Controls.FlingOutMode.Right)
            {
                if ((p_eBord & Controls.FlingOutMode.Left) == Controls.FlingOutMode.Left)
                {
                    oOrigine.X = 1;
                }
                else
                {
                    oOrigine.X = dContainerActualWidth - 1;
                }

                p_oVelocity.X = p_oVelocity.X * -1;
            }

            if ((p_eBord & Controls.FlingOutMode.Up) == Controls.FlingOutMode.Up
             || (p_eBord & Controls.FlingOutMode.Down) == Controls.FlingOutMode.Down)
            {
                if ((p_eBord & Controls.FlingOutMode.Up) == Controls.FlingOutMode.Up)
                {
                    oOrigine.Y = 1;
                }
                else
                {
                    oOrigine.Y = dContainerActualHeight - 1;
                }

                p_oVelocity.Y = p_oVelocity.Y * -1;
            }

            // On modifie l'dAngle d'inertie

            this.Center = oOrigine;

            this.m_oInertia.InitialOrigin = oOrigine;
            this.m_oInertia.InitialAngularVelocity = p_dAngularVelocity;
            this.m_oInertia.InitialVelocity = p_oVelocity;

            this.m_oInertia.DesiredDeceleration = 0.01;
            this.m_oInertia.DesiredExpansionDeceleration = 0.0001;

            // On la redémare

            this.m_oInertia.Begin();

            this.m_oInertia.Affine2DInertiaCompleted += new EventHandler<Affine2DOperationCompletedEventArgs>(this.Inertia_Affine2DInertiaCompleted);
            this.m_oInertia.Affine2DInertiaDelta += new System.EventHandler<Affine2DOperationDeltaEventArgs>(this.Inertia_Affine2DInertiaDelta);
        }

        /// <summary>
        /// Repositionne le contrôle et le centre sur un redimensionnement en centré.
        /// </summary>
        /// <param name="p_oSizeInfo">Informations sur le changement de taille.</param>
        private void ResizeCentered(SizeChangedInfo p_oSizeInfo)
        {
            // Récupération de la matrice actuelle

            Matrix oActual = (this.RenderTransform as MatrixTransform).Matrix;

            // Calcul de la transformation

            Matrix oMatrice = new Matrix();
            oMatrice.Rotate(this.Orientation);

            Point oTransform = oMatrice.Transform(new Point(((p_oSizeInfo.NewSize.Width - p_oSizeInfo.PreviousSize.Width) / 2) * -1, ((p_oSizeInfo.NewSize.Height - p_oSizeInfo.PreviousSize.Height) / 2) * -1));

            // On définit la transformation

            oActual.Translate(oTransform.X, oTransform.Y);

            // On redéfinit la matrice

            this.RenderTransform = new MatrixTransform(oActual);
        }

        /// <summary>
        /// Repositionne le contrôle et le centre sur un redimensionnement en continue.
        /// </summary>
        /// <param name="p_oSizeInfo">Informations sur le changement de taille</param>
        private void ResizeContinue(SizeChangedInfo p_oSizeInfo)
        {
            // On recalcul le centre (donc on divise les tailles par 2)

            Vector oDeplacement = Point.Subtract(new Point(p_oSizeInfo.NewSize.Width / 2, p_oSizeInfo.NewSize.Height / 2), new Point(p_oSizeInfo.PreviousSize.Width / 2, p_oSizeInfo.PreviousSize.Height / 2));

            // On applique la transformation

            Matrix oMatrice = new Matrix();
            oMatrice.Rotate(this.Orientation);

            // On calcul le vecteur de déplacement par rapport à la rotation

            Vector oTransform = oMatrice.Transform(oDeplacement);

            // On redéfinit le centre en ajoute le déplacement à son centre initial

            this.SetCenterNoRefresh(Point.Add(this.Center, oTransform));
        }

        /// <summary>
        /// Appelée sur la fin du Fling Out.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void StoryBoardComplete(object p_oSender, EventArgs p_oArgs)
        {
            // Event de fin

            this.RaiseFlingOutCompletedEvent();

            // On retire l'objet

            if (this.Container != null)
            {
                object oObjectToRemove = this;

                if (!this.Container.Items.Contains(this))
                    oObjectToRemove = this.Content;

                // Suppression de l'élément au bon endroit

                IList oSource = this.Container.ItemsSource as IList ?? this.Container.Items;
                oSource.Remove(oObjectToRemove);
            }
        }

        #endregion

        #region Fonctions statiques privées

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterView.Center" /> est modifiée.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private static void OnCenterChanged(DependencyObject p_oSender, DependencyPropertyChangedEventArgs p_oArgs)
        {
            ((CanvasScatterViewItem)p_oSender).OnCenterChanged((Point)p_oArgs.OldValue, (Point)p_oArgs.NewValue);
        }

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterView.Orientation" /> est modifiée.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private static void OnOrientationChanged(DependencyObject p_oSender, DependencyPropertyChangedEventArgs p_oArgs)
        {
            ((CanvasScatterViewItem)p_oSender).OnOrientationChanged((double)p_oArgs.OldValue, (double)p_oArgs.NewValue);
        }

        /// <summary>
        /// Appelée lorsque la propriété <see cref="P:Black.SurfaceToolkit.Controls.CanvasScatterView.Scale" /> est modifiée.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private static void OnScaleChanged(DependencyObject p_oSender, DependencyPropertyChangedEventArgs p_oArgs)
        {
            ((CanvasScatterViewItem)p_oSender).OnScaleChanged((double)p_oArgs.OldValue, (double)p_oArgs.NewValue);
        }

        #endregion
    }
}
